<?php

namespace common\helpers;

use Yii;
use yii\helpers\BaseStringHelper;
use yii\helpers\Json;

/**
 * 字符串扩展操作函数
 *
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class StringHelper extends BaseStringHelper {

    const PATTERN_NUMBER = '/\d+/';
    const PATTERN_MOBILE = '/1[3456789]{1}\d{9}$/';
    const PATTERN_EMAIL = '/^\w+([-.]\w+)*@([0-9a-z][0-9a-z-]+.)+[a-z]{2,3}$/';
    const PATTERN_IDCARDNO = '/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|x|X)$/';
    const CN_UNIT_WORD = ['万', '亿', '兆', '京', '垓', '秭', '穣', '沟', '涧', '正', '载', '极', '恒河沙', '阿僧祇', '那由他', '不可思议', '无量', '大数', '全仕祥', '亦', '王', '肤', '张', '张', '战'];

    public static function unitNumber($number, $min = 200, $append = '+') {
        if (!$number || $number <= $min) {
            return $number;
        }
        $level = 0;
        $tempNumber = $number;
        do {
            $level++;
            $tempNumber = intval($tempNumber / 10);
        } while ($tempNumber > 100);
        if ($level < 3) {
            return intval($number / 100) * 100;
        }
        $power = pow(10, $level);
        $unit = ($level - 3) % 4;
        $unitIndex = floor(($level - 3) / 4);
        if ($unit == 0) {
            $n = floor(round($number / $power, 2)) / 10;
        } else {
            $n = intval($number / $power * pow(10, $unit - 1));
        }
        return $n . static::CN_UNIT_WORD[$unitIndex] . $append;
    }

    public static function splitStringToArray($string, $splitChar = ',', $unique = true) {
        if (!is_string($string)) {
            return [];
        }
        $data = static::explode($string, $splitChar);
        if ($unique) {
            $data = array_unique($data);
        }
        if (empty($data)) {
            return [];
        }
        return $data;
    }

    public static function isSerialized($str) {
        if (!is_string($str)) {
            return false;
        }
        return $str == serialize(null) || $str == serialize(false) || @unserialize($str) !== false;
    }

    public static function unserialize($str) {
        if (static::isSerialized($str)) {
            return unserialize($str);
        }
        return $str;
    }

    public static function splitWords($string, $trim = true, $skipEmpty = false) {
//        $result = preg_split("/[\s|,-\\|\\.]/", $string);
        //$pattern = '/[\s|,|-|\||，|。]+/';
        //$result = preg_split($pattern, $string);
        //只保留汉字和数字和英文字符
        preg_match_all('/[a-zA-Z0-9\x{4e00}-\x{9fff}]+/u', $string, $matches);
        $result = $matches[0];
        if ($trim !== false) {
            if ($trim === true) {
                $trim = 'trim';
            } elseif (!is_callable($trim)) {
                $trim = function ($v) use ($trim) {
                    return trim($v, $trim);
                };
            }
            $result = array_map($trim, $result);
        }
        if ($skipEmpty) {
            // Wrapped with array_values to make array keys sequential after empty values removing
            $result = array_values(array_filter($result, function ($value) {
                return $value !== '';
            }));
        }
        return $result;
    }

    /**
     * 匹配禁送地区码
     *
     * @param array $list 地区
     * @param string $code 地区码
     * @return string 字符串
     */
    public static function matchAreaCode($list, $code) {
        if (empty($list)) {
            return false;
        }
        if (strlen($code) < 6) {
            $code = str_pad($code, 6, "0");
        }
        if (in_array($code, $list)) {
            return $code;
        }
        foreach ($list as $areacode) {
            $temp = str_split(str_pad((string) $areacode, 6, "0"), 2);
            while (count($temp) > 0) {
                if (array_pop($temp) * 1) {
                    break;
                }
                if (strpos($code, implode($temp)) !== false) {
                    return $areacode;
                }
            }
        }
        return false;
    }

    /**
     * 匹配禁送地区码
     *
     * @param array $list 地区
     * @param string $code 地区码
     * @return string 字符串
     */
    public static function matchAreaCode2($areacode, $code) {
        if ($areacode == $code) {
            return true;
        }
        $temp = str_split(str_pad($areacode, 6, "0"), 2);
        while (count($temp) > 0) {
            if (array_pop($temp) * 1) {
                break;
            }
            if (strpos($code, implode($temp)) !== false) {
                return true;
            }
        }
        return false;
    }

    /**
     * 生成随机字符串
     *
     * @param int $length 长度
     * @param boolean $numeric 是否返回数字
     * @return string 字符串
     */
    public static function random($length, $numeric = false, $easy = true) {
        $source = microtime();
        if (is_string($_SERVER['DOCUMENT_ROOT'])) {
            $source .= strval($_SERVER['DOCUMENT_ROOT']);
        }
        $hash = '';
        $string = '';
        if ($numeric) {
            $seed = base_convert(md5($source), 16, 10);
            $string = str_replace('0', '', $seed) . '012340567890';
        } else {
            $seed = base_convert(md5($source), 16, 35);
            $string = $seed . '3D7890VABCFRST105678902KE45JPQ012343DRST0H9E45JPAB012340LMN67878Q0129GHUV' . strtoupper($seed);
            $hash = chr(rand(1, 26) + rand(0, 1) * 32 + 64);
            $length--;
        }
        if ($easy) {
            $string = str_replace(['O', 'I', 'l', 'i'], ['0', '1', '2', '3'], $string);
        }
        $max = strlen($string) - 1;
        for ($i = 0; $i < $length; $i++) {
            $pos = mt_rand(0, $max);
            $hash .= substr($string, $pos, 1);
        }
        return $hash;
    }

    /**
     * 屏蔽字符串的一部分字符为指定字符
     * 
     * @param string $subject 字符串
     * @param integer $start 开始位置
     * @param integer $end 结束位置
     * @param string $char 替换字符
     * @param string $type 正则表达式
     * @return string
     */
    public static function maskChar($subject, $start = 3, $end = 4, $char = '*', $type = 'w') {
        $length = static::byteLength($subject);
        $masklength = $length - $start - $end;
        //\w
        $pattern_begin = '(\\' . $type . '{0,' . $start . '})';
        $pattern_end = '(\\' . $type . '{' . $end . '})';
        $pattern_body = '\\' . $type . '{' . $masklength . '}';
        $pattern = '/' . $pattern_begin . $pattern_body . $pattern_end . '/';
        $replacement = "\$1 " . str_pad('', $masklength, $char) . " \$2";
        return preg_replace($pattern, $replacement, $subject);
    }

    /**
     * 屏蔽字符串的一部分字符为指定字符
     * 
     * @param string $subject 字符串
     * @param integer $start 开始位置
     * @param integer $end 结束位置
     * @param string $maxMask 最大替换字符数量
     * @param string $char 替换字符
     * @return string
     */
    public static function mask($subject, $start = 3, $end = 4, $maxMask = false, $char = '*') {
        $words = static::mbStrSplit($subject);
        $length = count($words);
        $string = [];
        if ($end) {
            $end = $length - $end;
        } else {
            $end = $length;
        }
        if ($end - 1 < 1) {
            $end = $length;
        }
        $maxMaskLength = intval($maxMask ?: $length);
        $maskCount = 0;
        foreach ($words as $index => $word) {
            if ($index >= $start && $index < $end) {
                if ($maskCount < $maxMaskLength) {
                    $string[] = $char;
                    $maskCount++;
                }
            } else {
                $string[] = $word;
            }
        }
        return implode($string);
    }

    /**
     * 分割字符集字符串
     * 
     * @param string $color 16进制颜色值
     * @return array
     */
    public static function mbStrSplit($string, $len = 1) {
        $strlen = mb_strlen($string);
        for ($i = 0; $i < $strlen; $i += $len) {
            $array[] = mb_substr($string, $i, $len);
        }
        return $array;
    }

    /**
     * 十六进制转RGB
     * 
     * @param string $color 16进制颜色值
     * @return array
     */
    public static function hex2rgb($color) {
        $hexColor = str_replace('#', '', $color);
        $lens = strlen($hexColor);
        if ($lens != 3 && $lens != 6) {
            return false;
        }
        $newcolor = '';
        if ($lens == 3) {
            for ($i = 0; $i < $lens; $i++) {
                $newcolor .= $hexColor[$i] . $hexColor[$i];
            }
        } else {
            $newcolor = $hexColor;
        }
        $hex = str_split($newcolor, 2);
        $rgb = [];
        foreach ($hex as $key => $vls) {
            $rgb[] = hexdec($vls);
        }
        return $rgb;
    }

    public static function formatUnsign($string) {
        $char = "。、！？：；﹑•＂…‘’“”〝〞∕¦‖—　〈〉﹞﹝「」‹›〖〗】【»«』『〕〔》《﹐¸﹕︰﹔！¡？¿﹖﹌﹏﹋＇´ˊˋ―﹫︳︴¯＿￣﹢﹦﹤‐­˜﹟﹩﹠﹪﹡﹨﹍﹉﹎﹊ˇ︵︶︷︸︹︿﹀︺︽︾ˉ﹁﹂﹃﹄︻︼（）";
        $pattern = [
            "/[[:punct:]]/i", //英文标点符号
            '/[' . $char . ']/u', //中文标点符号
            '/[ ]{2,}/'
        ];
        return preg_replace($pattern, '_', $string);
    }

    /**
     * 正则表达式检测已有类型数据格式
     * 
     * @param string $content 检测内容
     * @param string $pattern 正则表达式
     * @return boolean 检测状态
     */
    public static function formatContent($content, $pattern) {
        if (preg_match($pattern, $content)) {
            return true;
        }
        return false;
    }

    /**
     * 检测手机号是否正确
     * 
     * @param string $text 检测内容
     * @return boolean 
     */
    public static function isNumber($text) {
        $pattern = ArrayHelper::getValue(Yii::$app->params, 'format_pattern.number', self::PATTERN_NUMBER);
        return self::formatContent($text, $pattern);
    }

    /**
     * 检测手机号是否正确
     * 
     * @param string $text 检测内容
     * @return boolean 
     */
    public static function isMobile($text) {
        $pattern = ArrayHelper::getValue(Yii::$app->params, 'format_pattern.mobile', self::PATTERN_MOBILE);
        return self::formatContent($text, $pattern);
    }

    /**
     * 检测邮箱是否正确
     * 
     * @param string $text 检测内容
     * @return boolean 
     */
    public static function isEmail($text) {
        $pattern = ArrayHelper::getValue(Yii::$app->params, 'format_pattern.email', self::PATTERN_EMAIL);
        return self::formatContent($text, $pattern);
    }

    /**
     * 检测身份证号是否正确
     * 
     * @param string $value 检测内容
     * @return boolean 检测状态
     */
    public static function isIdcardNumber($value, $exact = true) {
        if (!$exact) {
            $formatPattern = Yii::$app->params['format_pattern'];
            $pattern = isset($formatPattern['idcardno']) ? $formatPattern['idcardno'] : self::PATTERN_IDCARDNO;
            return self::formatContent($text, $pattern);
        }
        $provinceNumber = array(
            '11', '12', '13', '14', '15', '21', '22',
            '23', '31', '32', '33', '34', '35', '36',
            '37', '41', '42', '43', '44', '45', '46',
            '50', '51', '52', '53', '54', '61', '62',
            '63', '64', '65', '71', '81', '82', '91'
        );
        if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $value)) {
            return false;
        }
        if (!in_array(substr($value, 0, 2), $provinceNumber)) {
            return false;
        }
        $value = preg_replace('/[xX]$/i', 'a', $value);
        $length = strlen($value);
        if ($length == 18) {
            $birthday = substr($value, 6, 4) . '-' . substr($value, 10, 2) . '-' . substr($value, 12, 2);
        } else {
            $birthday = '19' . substr($value, 6, 2) . '-' . substr($value, 8, 2) . '-' . substr($value, 10, 2);
        }
        if (date('Y-m-d', strtotime($birthday)) != $birthday) {
            return false;
        }
        if ($length == 18) {
            $_sum = 0;
            for ($i = 17; $i >= 0; $i--) {
                $_subs = substr($value, 17 - $i, 1);
                $_sum += (pow(2, $i) % 11) * (($_subs == 'a') ? 10 : intval($_subs, 11));
            }
            if ($_sum % 11 != 1) {
                return false;
            }
        }
        return true;
    }

    /**
     * 数字转换为中文
     * @param  string|integer|float  $num  目标数字
     * @param  integer $mode 模式[true:金额（默认）,false:普通数字表示]
     * @param  boolean $sim 使用小写（默认）
     * @return string
     */
    function numberToZhcn($num, $mode = true, $sim = true) {
        if (!is_numeric($num)) {
            return '含有非数字非小数点字符！';
        }
        $char = $sim ? array('零', '一', '二', '三', '四', '五', '六', '七', '八', '九') : array('零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖');
        $unit = $sim ? array('', '十', '百', '千', '', '万', '亿', '兆') : array('', '拾', '佰', '仟', '', '萬', '億', '兆');
        $retval = $mode ? '元' : '点';
        //小数部分
        if (strpos($num, '.')) {
            list($num, $dec) = explode('.', $num);
            $dec = strval(round($dec, 2));
            if ($mode) {
                $retval .= "{$char[$dec['0']]}角{$char[$dec['1']]}分";
            } else {
                for ($i = 0, $c = strlen($dec); $i < $c; $i++) {
                    $retval .= $char[$dec[$i]];
                }
            }
        }
        //整数部分
        $str = $mode ? strrev(intval($num)) : strrev($num);
        for ($i = 0, $c = strlen($str); $i < $c; $i++) {
            $out[$i] = $char[$str[$i]];
            if ($mode) {
                $out[$i] .= $str[$i] != '0' ? $unit[$i % 4] : '';
                if ($i > 1 and $str[$i] + $str[$i - 1] == 0) {
                    $out[$i] = '';
                }
                if ($i % 4 == 0) {
                    $out[$i] .= $unit[4 + floor($i / 4)];
                }
            }
        }
        $retval = join('', array_reverse($out)) . $retval;
        return $retval;
    }

    public function unicodeDecode($string) {
        $json = '{"str":"' . $string . '"}';
        $arr = Json::decode($json);
        if (empty($arr)) {
            return '';
        }
        return $arr['str'];
    }

    /**
     * 模型生成订单号
     * @param string $from 
     * @param integer|float $float 
     * @param \yii\db\ActiveQueryInterface|null $query 
     * @return string 范围唯一的订单号
     */
    public static function generateOrderNo($from, $float = 0, $query = null, $attribute = '') {
        $pcode = 0;
        while ($float > 0 && $pcode < 10) {
            $float = floor($float / 10);
            $pcode++;
        }
        $date = date('Ymd');
        $rules = [
            'from' => str_pad(is_numeric($from) ? dechex($from) : $from, 2, "0", STR_PAD_RIGHT),
            'pcode' => $pcode,
            'date' => substr($date, 2),
        ];
        do {
            $micro = round((microtime(true) - strtotime($date)) * 10000);
            $rules['micro'] = str_pad($micro, 9, 0, STR_PAD_RIGHT);
            $tn = implode($rules);
            if ($query && $attribute && $query instanceof \yii\db\ActiveQueryInterface) {
                $record = $query->andWhere([
                    $attribute => $tn
                ])->exists();
            } else {
                $record = false;
            }
        } while ($record);
        return $tn;
    }

}
